﻿namespace Example.Modbus.Kernel;

public static class Extends
{
    /// <summary>
    /// CRC-16/AUG-CCITT 校验码的计算
    ///     1. 预置 1 个 16 位的寄存器为十六进制 FFFF(即全为 1),称此寄存器为 CRC 寄存器；
    ///     2. 把第一个8位二进制数据(既通讯信息帧的第一个字节)与 16 位的 CRC 寄存器的低8位相异或,把结果放于CRC寄存器；
    ///     3. 把 CRC 寄存器的内容右移一位(朝低位)用 0 填补最高位，并检查右移后的移出位；
    ///     4. 如果移出位为 0：重复第 3 步(再次右移一位)；如果移出位为 1：CRC 寄存器与多项式 A001(1010 0000 0000 0001)进行异或；
    ///     5. 重复步骤 3 和 4，直到右移 8 次，这样整个 8 位数据全部进行了处理；
    ///     6. 重复步骤 2 到步骤 5，进行通讯信息帧下一个字节的处理；
    ///     7. 将该通讯信息帧所有字节按上述步骤计算完成后，得到的 16 位 CRC 寄存器的高、低字节进行交换；
    ///     8.最后得到的 CRC 寄存器内容即为 CRC16 码。注意得到的 CRC 码即为低前高后顺序)
    /// </summary>
    public static byte[] CRC16(this byte[] p)
    {
        byte CRC16Lo, CRC16Hi, CL, CH, SaveHi, SaveLo;
        int i, Flag;
        CRC16Lo = 0xFF;
        CRC16Hi = 0xFF;
        CL = 0x01;
        CH = 0xA0;
        for (i = 0; i < p.Length; i++)
        {
            CRC16Lo ^= p[i];
            for (Flag = 0; Flag < 8; Flag++)
            {
                SaveHi = CRC16Hi;
                SaveLo = CRC16Lo;
                CRC16Hi >>= 1;
                CRC16Lo >>= 1;
                if ((SaveHi & 0x01) == 0x01)
                    CRC16Lo |= 0x80;
                if ((SaveLo & 0x01) == 0x01)
                {
                    CRC16Hi ^= CH;
                    CRC16Lo ^= CL;
                }
            }
        }
        byte[] crc = { CRC16Lo, CRC16Hi };
        return crc;
    }


    public static bool Check(this byte[] a, byte[] b)
    {
        if (a.Length != b.Length) return false;
        return !a.Where((t, i) => t != b[i]).Any();
    }
}